package com.toutoushidao.ssm.dao;

import com.mongodb.BasicDBObject;
import com.mongodb.MongoClient;
import com.mongodb.client.*;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import com.mongodb.util.JSON;
import com.toutoushidao.ssm.util.MongoHelper;
import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Repository
public class MongoDbDao {

    MongoHelper mongoHelper = new MongoHelper();
    MongoClient mongoClient = mongoHelper.getMongoClient();
    MongoDatabase mongoDataBase = mongoHelper.getMongoDataBase(mongoClient);

    private final Logger LOG = LoggerFactory.getLogger(this.getClass());

    /**
     * 查询一条数据
     *
     * @param table
     * @param doc
     */
    public Map<String, String> selectOne(String table, BasicDBObject doc) {
        MongoCollection<Document> collection = mongoDataBase.getCollection(table);
        LOG.debug("------------ DB 查询语句 --- db." + table + ".find(" + doc.toJson() + ") ------------");
        FindIterable<Document> iterable = collection.find(doc).projection(new BasicDBObject("_id", 0));
        Map<String, String> jsonStrToMap = null;
        MongoCursor<Document> cursor = iterable.iterator();
        while (cursor.hasNext()) {
            Document resultDoc = cursor.next();
            String jsonString = resultDoc.toJson();
            jsonStrToMap = jsonStrToMap(jsonString);
        }
        LOG.debug("------------ DB 查询结果 " + table + " ------ " + jsonStrToMap + " ------------");
        return jsonStrToMap;
    }

    /**
     * 查询一条Object数据
     *
     * @param table
     * @param findDoc
     * @param projectDoc
     */
    public Document selectObject(String table, BasicDBObject findDoc, BasicDBObject projectDoc) {
        MongoCollection<Document> collection = mongoDataBase.getCollection(table);
        LOG.debug("------------ DB 查询语句 --- db." + table + ".find(" + findDoc.toJson() + ") ------------");
        FindIterable<Document> iterable = collection.find(findDoc).projection(projectDoc);
        MongoCursor<Document> cursor = iterable.iterator();
        Document resultDoc = null;
        while (cursor.hasNext()) {
            resultDoc = cursor.next();
        }
        LOG.debug("------------ DB 查询结果 " + table + " ------ " + resultDoc + " ------------");
        return resultDoc;
    }

    /**
     * 查询列表数据，当doc为空时检索全部
     *
     * @param table
     * @param doc
     */
    public List<Map<String, String>> selectList(String table, BasicDBObject doc) {
        MongoCollection<Document> collection = mongoDataBase.getCollection(table);
        LOG.debug("------------ DB 查询语句 --- db." + table + ".find(" + doc.toJson() + ") ------------");
        FindIterable<Document> iterable = collection.find(doc);
        /** 1. 获取迭代器FindIterable<Document> 2. 获取游标MongoCursor<Document> 3. 通过游标遍历检索出的文档集合 */
        List<Map<String, String>> list = new ArrayList<Map<String, String>>();
        MongoCursor<Document> cursor = iterable.iterator();
        while (cursor.hasNext()) {
            Document resultDoc = cursor.next();
            String jsonString = resultDoc.toJson();
            Map<String, String> resultMap = jsonStrToMap(jsonString);
            list.add(resultMap);
        }
        LOG.debug("------------ DB 查询结果 " + table + " ------ " + list + " ------------");
        return list;
    }

    /**
     * 查询列表数据，当doc为空时检索全部
     *
     * @param table
     * @param findDoc
     * @param projectDoc
     */
    public List<Map<String, String>> selectList(String table, BasicDBObject findDoc, BasicDBObject projectDoc) {
        MongoCollection<Document> collection = mongoDataBase.getCollection(table);
        LOG.debug("------------ DB 查询语句 --- db." + table + ".find(" + findDoc.toJson() + ") ------------");
        FindIterable<Document> iterable = collection.find(findDoc).projection(projectDoc);
        /** 1. 获取迭代器FindIterable<Document> 2. 获取游标MongoCursor<Document> 3. 通过游标遍历检索出的文档集合 */
        List<Map<String, String>> list = new ArrayList<Map<String, String>>();
        MongoCursor<Document> cursor = iterable.iterator();
        while (cursor.hasNext()) {
            Document resultDoc = cursor.next();
            String jsonString = resultDoc.toJson();
            Map<String, String> resultMap = jsonStrToMap(jsonString);
            list.add(resultMap);
        }
        LOG.debug("------------ DB 查询结果 " + table + " ------ " + list + " ------------");
        return list;
    }

    /**
     * 检索全部并返回迭代器
     *
     * @param table
     */
    public List<Map<String, String>> selectAll(String table) {
        LOG.debug("------------ DB 查询语句 --- db." + table + ".find() ------------");
        MongoCollection<Document> collection = mongoDataBase.getCollection(table);
        FindIterable<Document> iterable = collection.find().projection(new BasicDBObject("_id", 0));
        List<Map<String, String>> list = new ArrayList<Map<String, String>>();
        MongoCursor<Document> cursor = iterable.iterator();
        while (cursor.hasNext()) {
            Document resultDoc = cursor.next();
            String jsonString = resultDoc.toJson();
            Map<String, String> resultList = jsonStrToMap(jsonString);
            list.add(resultList);
        }
        LOG.debug("------------ DB 查询结果 " + table + " ------ " + list + " ------------");
        return list;
    }

    /**
     * 固定数量排序与分页
     *
     * @param table
     * @param findDoc
     * @param sortDoc
     */
    public List<Document> selectSortPage(String table, BasicDBObject findDoc,
                                         BasicDBObject projectionDoc, BasicDBObject sortDoc) {
        MongoCollection<Document> collection = mongoDataBase.getCollection(table);
        FindIterable<Document> iterable = collection.find(findDoc).projection(projectionDoc).sort(sortDoc);
        List<Document> list = new ArrayList<>();
        MongoCursor<Document> cursor = iterable.iterator();
        while (cursor.hasNext()) {
            list.add(cursor.next());
        }
        return list;
    }

    /**
     * 固定数量排序与分页
     *
     * @param table
     * @param findDoc
     * @param sortDoc
     */
    public List<Document> selectSortPage(String table, BasicDBObject findDoc, BasicDBObject projectionDoc,
                                                    BasicDBObject sortDoc, int page, int pageSize) {
        MongoCollection<Document> collection = mongoDataBase.getCollection(table);
        FindIterable<Document> iterable = collection.find(findDoc).projection(projectionDoc).sort(sortDoc);
        if (pageSize != 0) {
            iterable = iterable.skip(page * pageSize).limit(pageSize);
        }
//        Map<String, String> jsonStrToMap = null;
        List<Document> list = new ArrayList<>();
        MongoCursor<Document> cursor = iterable.iterator();
        while (cursor.hasNext()) {
            list.add(cursor.next());
        }
        return list;
    }

    /**
     * 查询结果计数
     *
     * @param table
     * @param findDoc
     * @return
     */
    public long count(String table, BasicDBObject findDoc) {
        MongoCollection<Document> collection = mongoDataBase.getCollection(table);
        FindIterable<Document> iterable = collection.find(findDoc);
        MongoCursor<Document> cursor = iterable.iterator();
        return collection.count(findDoc);
    }

    /**
     * 遍历迭代器FindIterable<Document>
     */
    public void printFindIterable(FindIterable<Document> iterable) {
        MongoCursor<Document> cursor = iterable.iterator();
        while (cursor.hasNext()) {
            Document resultDoc = cursor.next();
            System.out.println(resultDoc.toJson());
            LOG.debug("------------ DB 遍历迭代器 " + resultDoc.toJson() + " ------------");
        }
        cursor.close();
    }

    public boolean insert(String table, Document document) {
        LOG.debug("------------ DB 插入语句 --- db." + table + ".insert(" + document.toJson() + ") ------------");
        MongoCollection<Document> collection = mongoDataBase.getCollection(table);
        collection.insertOne(document);
        long count = collection.count(document);
        LOG.debug("------------ DB 插入结果 " + table + " ------ " + count + " ------------");
        if (count == 1) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * insert many
     *
     * @param table
     */
    public boolean insertMany(String table, List<Document> documents) {
        LOG.debug("------------ DB 插入语句 --- db." + table + ".insert(" + documents.toString() + ") ------------");
        MongoCollection<Document> collection = mongoDataBase.getCollection(table);
        long preCount = collection.count();
        collection.insertMany(documents);
        long nowCount = collection.count();
        LOG.debug("------------ DB 插入结果 " + table + " ------ "
                + (nowCount - preCount) + "/" + documents.size() + " ------------");
        if ((nowCount - preCount) == documents.size()) {
            return true;
        } else {
            return false;
        }
    }

    public boolean delete(String table, BasicDBObject document) {
        LOG.debug("------------ DB 删除语句 --- db." + table + ".delete(" + document.toJson() + ") ------------");
        MongoCollection<Document> collection = mongoDataBase.getCollection(table);
        DeleteResult deleteManyResult = collection.deleteMany(document);
        long deletedCount = deleteManyResult.getDeletedCount();
        LOG.debug("------------ DB 删除结果 " + table + " ------ " + deletedCount + " ------------");
        if (deletedCount > 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 删除一个
     *
     * @param table
     * @param document
     */
    public boolean deleteOne(String table, BasicDBObject document) {
        LOG.debug("------------ DB 删除语句 --- db." + table + ".delete(" + document.toJson() + ") ------------");
        MongoCollection<Document> collection = mongoDataBase.getCollection(table);
        DeleteResult deleteOneResult = collection.deleteOne(document);
        long deletedCount = deleteOneResult.getDeletedCount();
        LOG.debug("------------ DB 删除结果 " + table + " ------ " + deletedCount + " ------------");
        if (deletedCount == 1) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * SET更新
     *
     * @param table
     * @param whereDoc
     * @param updateDoc
     * @return
     */
    public boolean update(String table, BasicDBObject whereDoc, BasicDBObject updateDoc) {
        LOG.debug("------------ DB 更新语句 --- db." + table
                + ".update(" + whereDoc.toJson() + ", " + updateDoc + ") ------------");
        MongoCollection<Document> collection = mongoDataBase.getCollection(table);
        UpdateResult updateManyResult =
                collection.updateMany(whereDoc, new Document("$set", updateDoc));
        long modifiedCount = updateManyResult.getModifiedCount();
        LOG.debug("------------ DB 更新结果 " + table + " ------ " + modifiedCount + " ------------");
        if (modifiedCount > 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * INC更新
     *
     * @param table
     * @param whereDoc
     * @param incDoc
     * @return
     */
    public boolean updateInc(String table, BasicDBObject whereDoc, BasicDBObject incDoc) {
        LOG.debug("------------ DB 更新语句 --- db." + table
                + ".update(" + whereDoc.toJson() + ", " + incDoc + ") ------------");
        MongoCollection<Document> collection = mongoDataBase.getCollection(table);
        UpdateResult updateManyResult =
                collection.updateMany(whereDoc, new Document("$inc", incDoc));
        long modifiedCount = updateManyResult.getModifiedCount();
        LOG.debug("------------ DB 更新结果 " + table + " ------ " + modifiedCount + " ------------");
        if (modifiedCount > 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * update one Data
     *
     * @param table
     * @param whereDoc
     * @param updateDoc
     */
    public boolean updateOne(String table, BasicDBObject whereDoc, BasicDBObject updateDoc) {
        LOG.debug("------------ DB 更新语句 --- db." + table
                + ".update(" + whereDoc.toJson() + ", " + updateDoc + ") ------------");
        MongoCollection<Document> collection = mongoDataBase.getCollection(table);
        UpdateResult updateOneResult = collection.updateOne(whereDoc, new Document("$set", updateDoc));
        long modifiedCount = updateOneResult.getModifiedCount();
        LOG.debug("------------ DB 更新结果 " + table + " ------ " + modifiedCount + " ------------");
        if (modifiedCount == 1) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * update one Data
     *
     * @param table
     * @param whereDoc
     * @param updateDoc
     * @param addDoc
     */
    public boolean addToSet(String table, BasicDBObject whereDoc, BasicDBObject updateDoc, BasicDBObject addDoc) {
        LOG.debug("------------ DB 更新语句 --- db." + table
                + ".update(" + whereDoc.toJson() + ", " + updateDoc + ") ------------");
        MongoCollection<Document> collection = mongoDataBase.getCollection(table);
        Document dbDoc = new Document("$set", updateDoc);
        if (addDoc != null) {
            dbDoc.put("$addToSet", addDoc);
        }
        UpdateResult updateOneResult = collection.updateOne(whereDoc, dbDoc);
        long modifiedCount = updateOneResult.getModifiedCount();
        LOG.debug("------------ DB 更新结果 " + table + " ------ " + modifiedCount + " ------------");
        if (modifiedCount == 1) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * update one Data
     *
     * @param table
     * @param whereDoc
     * @param updateDoc
     * @param addDoc
     */
    public boolean addOrPull(String table, BasicDBObject whereDoc, BasicDBObject updateDoc, BasicDBObject addDoc, BasicDBObject pullDoc) {
        LOG.debug("------------ DB 更新语句 --- db." + table
                + ".update(" + whereDoc.toJson() + ", " + updateDoc + ") ------------");
        MongoCollection<Document> collection = mongoDataBase.getCollection(table);
        Document dbDoc = new Document("$set", updateDoc);
        if (addDoc != null) {
            dbDoc.put("$addToSet", addDoc);
        }
        if (pullDoc != null) {
            dbDoc.put("$pull", pullDoc);
        }
        UpdateResult updateOneResult = collection.updateOne(whereDoc, dbDoc);
        long modifiedCount = updateOneResult.getModifiedCount();
        LOG.debug("------------ DB 更新结果 " + table + " ------ " + modifiedCount + " ------------");
        if (modifiedCount == 1) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * create collection
     *
     * @param table
     */
    public void createCol(String table) {
        mongoDataBase.createCollection(table);
        LOG.debug("------------ DB 集合创建成功 " + table + " ------------");
    }

    /**
     * drop a collection
     *
     * @param table
     */
    public void dropCol(String table) {
        mongoDataBase.getCollection(table).drop();
        LOG.debug("------------ DB 集合删除成功 " + table + " ------------");
    }

    public static Map<String, String> jsonStrToMap(String jsonString) {
        Object parseObj = JSON.parse(jsonString); // 反序列化 把json 转化为对象
        Map<String, String> map = (Map<String, String>) parseObj; // 把对象转化为map
        return map;
    }

    /**
     * 聚合查询
     * <p>
     * //     * @param beginDate 开始日期
     * //     * @param table     集合名称
     * //     * @param matchDoc  匹配查询条件
     * //     * @param endDate   结束日期
     *
     * @return {"ManualLeave":2,"AutoLeave":4}
     * @throws Exception
     */
    public MongoCursor<Document> aggregate(String table, List<Document> conditionList) {
        MongoCollection<Document> collection = mongoDataBase.getCollection(table);
//        Document match = new Document("$match", matchDoc);
//        Document group = new Document("$group", groupDoc);
//        Document sort = new Document("$sort", new Document("_id", 1));
//        List<Document> aggregateList = new ArrayList<>();
//        aggregateList.add(match);
//        aggregateList.add(group);
//        aggregateList.add(sort);
        AggregateIterable<Document> resultSet = collection.aggregate(conditionList);
        MongoCursor<Document> cursor = resultSet.iterator();
//        List<Map<String, String>> list = new ArrayList<Map<String, String>>();
//        while (cursor.hasNext()) {
//            Document resultDoc = cursor.next();
//            String jsonString = resultDoc.toJson();
//            JSONObject.toJSON(cursor);
//            Map<String, String> resultMap = jsonStrToMap(jsonString);
//            list.add(resultMap);
//        }
        return cursor;
    }
}
